# -*- coding: utf-8 -*-
"""
通用助手函数模块

提供各种通用的辅助功能，包括：
- 文件操作助手
- 数据处理助手
- 字符串处理助手
- 日期时间助手
- 网络请求助手
- 系统信息助手
- 缓存助手
- 工具函数
"""

import os
import sys
import json
import hashlib
import secrets
import string
import shutil
import tempfile
import urllib.parse
from pathlib import Path
from typing import Any, Dict, List, Optional, Union, Callable, Tuple
from datetime import datetime, date, timedelta
from contextlib import contextmanager
from functools import wraps
import time
import logging

from ..config.settings import get_settings


logger = logging.getLogger(__name__)


class FileHelper:
    """文件操作助手"""
    
    @staticmethod
    def ensure_directory(path: Union[str, Path]) -> Path:
        """确保目录存在"""
        path = Path(path)
        path.mkdir(parents=True, exist_ok=True)
        return path
    
    @staticmethod
    def safe_filename(filename: str) -> str:
        """生成安全的文件名"""
        # 移除危险字符
        filename = filename.replace('/', '_').replace('\\', '_').replace(':', '_')
        filename = filename.replace('*', '_').replace('?', '_').replace('"', '_')
        filename = filename.replace('<', '_').replace('>', '_').replace('|', '_')
        
        # 限制长度
        if len(filename) > 200:
            name, ext = os.path.splitext(filename)
            filename = name[:200-len(ext)] + ext
        
        return filename
    
    @staticmethod
    def get_file_hash(file_path: Union[str, Path], algorithm: str = 'md5') -> str:
        """获取文件哈希值"""
        path = Path(file_path)
        if not path.exists():
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        hash_func = hashlib.new(algorithm)
        with open(path, 'rb') as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_func.update(chunk)
        
        return hash_func.hexdigest()
    
    @staticmethod
    def get_file_size(file_path: Union[str, Path]) -> int:
        """获取文件大小"""
        path = Path(file_path)
        if not path.exists():
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        return path.stat().st_size
    
    @staticmethod
    def copy_file(src: Union[str, Path], dst: Union[str, Path], overwrite: bool = False) -> bool:
        """复制文件"""
        src_path = Path(src)
        dst_path = Path(dst)
        
        if not src_path.exists():
            raise FileNotFoundError(f"源文件不存在: {src}")
        
        if dst_path.exists() and not overwrite:
            raise FileExistsError(f"目标文件已存在: {dst}")
        
        # 确保目标目录存在
        dst_path.parent.mkdir(parents=True, exist_ok=True)
        
        shutil.copy2(src_path, dst_path)
        return True
    
    @staticmethod
    def move_file(src: Union[str, Path], dst: Union[str, Path], overwrite: bool = False) -> bool:
        """移动文件"""
        src_path = Path(src)
        dst_path = Path(dst)
        
        if not src_path.exists():
            raise FileNotFoundError(f"源文件不存在: {src}")
        
        if dst_path.exists() and not overwrite:
            raise FileExistsError(f"目标文件已存在: {dst}")
        
        # 确保目标目录存在
        dst_path.parent.mkdir(parents=True, exist_ok=True)
        
        shutil.move(str(src_path), str(dst_path))
        return True
    
    @staticmethod
    def delete_file(file_path: Union[str, Path]) -> bool:
        """删除文件"""
        path = Path(file_path)
        if path.exists():
            if path.is_file():
                path.unlink()
            elif path.is_dir():
                shutil.rmtree(path)
            return True
        return False
    
    @staticmethod
    def read_json(file_path: Union[str, Path]) -> Dict[str, Any]:
        """读取JSON文件"""
        path = Path(file_path)
        if not path.exists():
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        with open(path, 'r', encoding='utf-8') as f:
            return json.load(f)
    
    @staticmethod
    def write_json(file_path: Union[str, Path], data: Dict[str, Any], indent: int = 2) -> bool:
        """写入JSON文件"""
        path = Path(file_path)
        path.parent.mkdir(parents=True, exist_ok=True)
        
        with open(path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=indent)
        
        return True
    
    @staticmethod
    @contextmanager
    def temporary_file(suffix: str = None, prefix: str = None, dir: str = None):
        """临时文件上下文管理器"""
        fd, path = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir)
        try:
            os.close(fd)
            yield path
        finally:
            if os.path.exists(path):
                os.unlink(path)
    
    @staticmethod
    @contextmanager
    def temporary_directory(suffix: str = None, prefix: str = None, dir: str = None):
        """临时目录上下文管理器"""
        temp_dir = tempfile.mkdtemp(suffix=suffix, prefix=prefix, dir=dir)
        try:
            yield temp_dir
        finally:
            shutil.rmtree(temp_dir)


class DataHelper:
    """数据处理助手"""
    
    @staticmethod
    def deep_merge(dict1: Dict[str, Any], dict2: Dict[str, Any]) -> Dict[str, Any]:
        """深度合并字典"""
        result = dict1.copy()
        
        for key, value in dict2.items():
            if key in result and isinstance(result[key], dict) and isinstance(value, dict):
                result[key] = DataHelper.deep_merge(result[key], value)
            else:
                result[key] = value
        
        return result
    
    @staticmethod
    def flatten_dict(data: Dict[str, Any], separator: str = '.') -> Dict[str, Any]:
        """扁平化字典"""
        def _flatten(obj, parent_key=''):
            items = []
            if isinstance(obj, dict):
                for k, v in obj.items():
                    new_key = f"{parent_key}{separator}{k}" if parent_key else k
                    items.extend(_flatten(v, new_key).items())
            elif isinstance(obj, list):
                for i, v in enumerate(obj):
                    new_key = f"{parent_key}{separator}{i}" if parent_key else str(i)
                    items.extend(_flatten(v, new_key).items())
            else:
                return {parent_key: obj}
            return dict(items)
        
        return _flatten(data)
    
    @staticmethod
    def group_by(data: List[Dict[str, Any]], key: str) -> Dict[str, List[Dict[str, Any]]]:
        """按指定键分组数据"""
        result = {}
        for item in data:
            group_key = item.get(key)
            if group_key not in result:
                result[group_key] = []
            result[group_key].append(item)
        return result
    
    @staticmethod
    def sort_by(data: List[Dict[str, Any]], key: str, reverse: bool = False) -> List[Dict[str, Any]]:
        """按指定键排序数据"""
        return sorted(data, key=lambda x: x.get(key, ''), reverse=reverse)
    
    @staticmethod
    def filter_by(data: List[Dict[str, Any]], filters: Dict[str, Any]) -> List[Dict[str, Any]]:
        """按条件过滤数据"""
        result = []
        for item in data:
            match = True
            for filter_key, filter_value in filters.items():
                if item.get(filter_key) != filter_value:
                    match = False
                    break
            if match:
                result.append(item)
        return result
    
    @staticmethod
    def paginate(data: List[Any], page: int, per_page: int) -> Dict[str, Any]:
        """分页数据"""
        total = len(data)
        start = (page - 1) * per_page
        end = start + per_page
        
        items = data[start:end]
        total_pages = (total + per_page - 1) // per_page
        
        return {
            'items': items,
            'total': total,
            'page': page,
            'per_page': per_page,
            'total_pages': total_pages,
            'has_prev': page > 1,
            'has_next': page < total_pages
        }
    
    @staticmethod
    def remove_duplicates(data: List[Dict[str, Any]], key: str) -> List[Dict[str, Any]]:
        """根据指定键去重"""
        seen = set()
        result = []
        for item in data:
            key_value = item.get(key)
            if key_value not in seen:
                seen.add(key_value)
                result.append(item)
        return result
    
    @staticmethod
    def transform_keys(data: Dict[str, Any], key_mapping: Dict[str, str]) -> Dict[str, Any]:
        """转换字典键名"""
        result = {}
        for old_key, value in data.items():
            new_key = key_mapping.get(old_key, old_key)
            result[new_key] = value
        return result
    
    @staticmethod
    def pick_fields(data: Dict[str, Any], fields: List[str]) -> Dict[str, Any]:
        """选择指定字段"""
        return {field: data.get(field) for field in fields if field in data}
    
    @staticmethod
    def omit_fields(data: Dict[str, Any], fields: List[str]) -> Dict[str, Any]:
        """排除指定字段"""
        return {key: value for key, value in data.items() if key not in fields}


class StringHelper:
    """字符串处理助手"""
    
    @staticmethod
    def generate_random_string(length: int = 8, chars: str = None) -> str:
        """生成随机字符串"""
        if chars is None:
            chars = string.ascii_letters + string.digits
        return ''.join(secrets.choice(chars) for _ in range(length))
    
    @staticmethod
    def slugify(text: str) -> str:
        """生成URL友好的字符串"""
        # 转换为小写
        text = text.lower()
        
        # 替换空格为连字符
        text = text.replace(' ', '-')
        
        # 移除特殊字符
        import re
        text = re.sub(r'[^a-z0-9\-]', '', text)
        
        # 移除多余的连字符
        text = re.sub(r'-+', '-', text)
        
        # 移除首尾连字符
        text = text.strip('-')
        
        return text
    
    @staticmethod
    def camel_to_snake(name: str) -> str:
        """驼峰转蛇形"""
        import re
        s1 = re.sub('(.)([A-Z][a-z]+)', r'\\1_\\2', name)
        return re.sub('([a-z0-9])([A-Z])', r'\\1_\\2', s1).lower()
    
    @staticmethod
    def snake_to_camel(name: str) -> str:
        """蛇形转驼峰"""
        components = name.split('_')
        return components[0] + ''.join(x.title() for x in components[1:])
    
    @staticmethod
    def is_chinese(text: str) -> bool:
        """判断是否包含中文"""
        import re
        return bool(re.search(r'[\\u4e00-\\u9fff]', text))
    
    @staticmethod
    def extract_numbers(text: str) -> List[str]:
        """提取数字"""
        import re
        return re.findall(r'\\d+', text)
    
    @staticmethod
    def extract_emails(text: str) -> List[str]:
        """提取邮箱地址"""
        import re
        pattern = r'\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b'
        return re.findall(pattern, text)
    
    @staticmethod
    def extract_phones(text: str) -> List[str]:
        """提取手机号"""
        import re
        pattern = r'1[3-9]\\d{9}'
        return re.findall(pattern, text)
    
    @staticmethod
    def clean_html(html: str) -> str:
        """清理HTML标签"""
        import re
        clean = re.compile('<.*?>')
        return re.sub(clean, '', html)
    
    @staticmethod
    def highlight_keywords(text: str, keywords: List[str], highlight_format: str = '<mark>{}</mark>') -> str:
        """高亮关键词"""
        result = text
        for keyword in keywords:
            if keyword in result:
                highlighted = highlight_format.format(keyword)
                result = result.replace(keyword, highlighted)
        return result


class DateTimeHelper:
    """日期时间助手"""
    
    @staticmethod
    def get_current_timestamp() -> int:
        """获取当前时间戳"""
        return int(time.time())
    
    @staticmethod
    def get_current_datetime() -> datetime:
        """获取当前日期时间"""
        return datetime.now()
    
    @staticmethod
    def get_current_date() -> date:
        """获取当前日期"""
        return date.today()
    
    @staticmethod
    def parse_datetime(datetime_str: str, format_str: str = '%Y-%m-%d %H:%M:%S') -> datetime:
        """解析日期时间字符串"""
        return datetime.strptime(datetime_str, format_str)
    
    @staticmethod
    def format_datetime(dt: datetime, format_str: str = '%Y-%m-%d %H:%M:%S') -> str:
        """格式化日期时间"""
        return dt.strftime(format_str)
    
    @staticmethod
    def get_date_range(start_date: date, end_date: date) -> List[date]:
        """获取日期范围"""
        dates = []
        current = start_date
        while current <= end_date:
            dates.append(current)
            current += timedelta(days=1)
        return dates
    
    @staticmethod
    def get_weekday_name(dt: datetime, locale: str = 'zh') -> str:
        """获取星期名称"""
        weekdays = {
            'zh': ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'],
            'en': ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
        }
        return weekdays.get(locale, weekdays['zh'])[dt.weekday()]
    
    @staticmethod
    def get_month_name(dt: datetime, locale: str = 'zh') -> str:
        """获取月份名称"""
        months = {
            'zh': ['一月', '二月', '三月', '四月', '五月', '六月', 
                   '七月', '八月', '九月', '十月', '十一月', '十二月'],
            'en': ['January', 'February', 'March', 'April', 'May', 'June',
                   'July', 'August', 'September', 'October', 'November', 'December']
        }
        return months.get(locale, months['zh'])[dt.month - 1]
    
    @staticmethod
    def is_weekend(dt: datetime) -> bool:
        """判断是否为周末"""
        return dt.weekday() >= 5
    
    @staticmethod
    def is_workday(dt: datetime) -> bool:
        """判断是否为工作日"""
        return not DateTimeHelper.is_weekend(dt)
    
    @staticmethod
    def get_age(birth_date: date, reference_date: date = None) -> int:
        """计算年龄"""
        if reference_date is None:
            reference_date = date.today()
        
        age = reference_date.year - birth_date.year
        if (reference_date.month, reference_date.day) < (birth_date.month, birth_date.day):
            age -= 1
        
        return age
    
    @staticmethod
    def get_quarter(dt: datetime) -> int:
        """获取季度"""
        return (dt.month - 1) // 3 + 1
    
    @staticmethod
    def get_week_of_year(dt: datetime) -> int:
        """获取年中第几周"""
        return dt.isocalendar()[1]


class NetworkHelper:
    """网络请求助手"""
    
    @staticmethod
    def encode_url(url: str) -> str:
        """URL编码"""
        return urllib.parse.quote(url)
    
    @staticmethod
    def decode_url(encoded_url: str) -> str:
        """URL解码"""
        return urllib.parse.unquote(encoded_url)
    
    @staticmethod
    def parse_url(url: str) -> Dict[str, str]:
        """解析URL"""
        parsed = urllib.parse.urlparse(url)
        return {
            'scheme': parsed.scheme,
            'netloc': parsed.netloc,
            'path': parsed.path,
            'params': parsed.params,
            'query': parsed.query,
            'fragment': parsed.fragment
        }
    
    @staticmethod
    def build_url(base_url: str, params: Dict[str, Any]) -> str:
        """构建URL"""
        if not params:
            return base_url
        
        query_string = urllib.parse.urlencode(params)
        separator = '&' if '?' in base_url else '?'
        return f"{base_url}{separator}{query_string}"
    
    @staticmethod
    def get_domain(url: str) -> str:
        """获取域名"""
        parsed = urllib.parse.urlparse(url)
        return parsed.netloc
    
    @staticmethod
    def is_valid_url(url: str) -> bool:
        """验证URL格式"""
        try:
            result = urllib.parse.urlparse(url)
            return all([result.scheme, result.netloc])
        except Exception:
            return False


class SystemHelper:
    """系统信息助手"""
    
    @staticmethod
    def get_platform() -> str:
        """获取操作系统平台"""
        return sys.platform
    
    @staticmethod
    def get_python_version() -> str:
        """获取Python版本"""
        return sys.version
    
    @staticmethod
    def get_memory_usage() -> Dict[str, int]:
        """获取内存使用情况"""
        try:
            import psutil
            memory = psutil.virtual_memory()
            return {
                'total': memory.total,
                'available': memory.available,
                'percent': memory.percent,
                'used': memory.used,
                'free': memory.free
            }
        except ImportError:
            return {}
    
    @staticmethod
    def get_disk_usage(path: str = '/') -> Dict[str, int]:
        """获取磁盘使用情况"""
        try:
            import psutil
            disk = psutil.disk_usage(path)
            return {
                'total': disk.total,
                'used': disk.used,
                'free': disk.free,
                'percent': (disk.used / disk.total) * 100
            }
        except ImportError:
            return {}
    
    @staticmethod
    def get_cpu_info() -> Dict[str, Any]:
        """获取CPU信息"""
        try:
            import psutil
            return {
                'count': psutil.cpu_count(),
                'percent': psutil.cpu_percent(interval=1),
                'freq': psutil.cpu_freq()._asdict() if psutil.cpu_freq() else None
            }
        except ImportError:
            return {}
    
    @staticmethod
    def get_process_info() -> Dict[str, Any]:
        """获取进程信息"""
        try:
            import psutil
            process = psutil.Process()
            return {
                'pid': process.pid,
                'name': process.name(),
                'memory_percent': process.memory_percent(),
                'cpu_percent': process.cpu_percent(),
                'create_time': process.create_time(),
                'status': process.status()
            }
        except ImportError:
            return {}


class CacheHelper:
    """缓存助手"""
    
    def __init__(self):
        self._cache = {}
    
    def get(self, key: str) -> Any:
        """获取缓存"""
        if key in self._cache:
            item = self._cache[key]
            if 'expires' not in item or item['expires'] > time.time():
                return item['value']
            else:
                del self._cache[key]
        return None
    
    def set(self, key: str, value: Any, ttl: int = None) -> None:
        """设置缓存"""
        item = {'value': value}
        if ttl:
            item['expires'] = time.time() + ttl
        self._cache[key] = item
    
    def delete(self, key: str) -> bool:
        """删除缓存"""
        if key in self._cache:
            del self._cache[key]
            return True
        return False
    
    def clear(self) -> None:
        """清空缓存"""
        self._cache.clear()
    
    def exists(self, key: str) -> bool:
        """检查缓存是否存在"""
        return self.get(key) is not None
    
    def size(self) -> int:
        """获取缓存大小"""
        return len(self._cache)


# 装饰器工具
def timer(func):
    """计时装饰器"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        logger.info(f"{func.__name__} 执行时间: {end_time - start_time:.4f}秒")
        return result
    return wrapper


def retry(max_attempts: int = 3, delay: float = 1.0, backoff: float = 2.0):
    """重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            attempts = 0
            current_delay = delay
            
            while attempts < max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    if attempts >= max_attempts:
                        logger.error(f"{func.__name__} 重试 {max_attempts} 次后仍然失败: {e}")
                        raise e
                    
                    logger.warning(f"{func.__name__} 第 {attempts} 次尝试失败: {e}，{current_delay}秒后重试")
                    time.sleep(current_delay)
                    current_delay *= backoff
            
            return None
        return wrapper
    return decorator


def memoize(ttl: int = None):
    """记忆化装饰器"""
    def decorator(func):
        cache = {}
        
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 创建缓存键
            key = str(args) + str(sorted(kwargs.items()))
            
            # 检查缓存
            if key in cache:
                item = cache[key]
                if ttl is None or item['timestamp'] + ttl > time.time():
                    return item['result']
                else:
                    del cache[key]
            
            # 计算结果并缓存
            result = func(*args, **kwargs)
            cache[key] = {
                'result': result,
                'timestamp': time.time()
            }
            
            return result
        
        return wrapper
    return decorator


# 全局实例
file_helper = FileHelper()
data_helper = DataHelper()
string_helper = StringHelper()
datetime_helper = DateTimeHelper()
network_helper = NetworkHelper()
system_helper = SystemHelper()
cache_helper = CacheHelper()


# 便捷函数
def ensure_dir(path: Union[str, Path]) -> Path:
    """确保目录存在"""
    return file_helper.ensure_directory(path)


def safe_filename(filename: str) -> str:
    """生成安全的文件名"""
    return file_helper.safe_filename(filename)


def random_string(length: int = 8) -> str:
    """生成随机字符串"""
    return string_helper.generate_random_string(length)


def current_timestamp() -> int:
    """获取当前时间戳"""
    return datetime_helper.get_current_timestamp()


def parse_url(url: str) -> Dict[str, str]:
    """解析URL"""
    return network_helper.parse_url(url)


def deep_merge(dict1: Dict[str, Any], dict2: Dict[str, Any]) -> Dict[str, Any]:
    """深度合并字典"""
    return data_helper.deep_merge(dict1, dict2)


def paginate_data(data: List[Any], page: int, per_page: int) -> Dict[str, Any]:
    """分页数据"""
    return data_helper.paginate(data, page, per_page)


def get_age(birth_date: date) -> int:
    """计算年龄"""
    return datetime_helper.get_age(birth_date)


def is_valid_url(url: str) -> bool:
    """验证URL格式"""
    return network_helper.is_valid_url(url)